import sys
import _mathlink
import MathematicaIO
from types import *
import Numeric


## You'll need to change this to start Mathematica if you're not using Windows

if sys.platform == "win32":
    PROGRAM="/Program Files/Wolfram Research/Mathematica/3.0/MathKernel.exe -mathlink"
elif sys.platform == "linux-i386" or sys.platform == "linux2":
    PROGRAM="/usr/local/mathematica/Executables/MathKernel -mathlink"

class Mathematica:
    def __init__(self, name="None", arg="-linkname", program=PROGRAM):
	self.name = name
	self.arg = arg
	self.program = program 

	_mathlink.init(self.name, self.arg, self.program)

    def finish(self):
	_mathlink.finish()

    def evaluate(self, expression):
	return expression.evaluate()

    def process(self):
	response = MathematicaIO.MathematicaResponse()
	return response.process()


ArrayType = type(Numeric.zeros((0,)))
## convert a python array to a List(of Lists...)
## rather ugly and probably unnecessary
def convertArray(array):
    args = []
    for i in range(len(array)):
	if type(array[i]) is ListType or type(array[i]) is ArrayType:
	    args.append(convertArray(array[i]),)
	else:
	    args.append(array[i])
    tmp = MathematicaFunction("List", args)
    return tmp


class MathematicaUnit:
    def __getitem__(self, second):
	return MathematicaFunction("Part", [self, second])

    def __abs__(self):
	return MathematicaFunction("Abs", [self, second])

    def __add__(self, second):
	return MathematicaFunction("Plus", [self, second])

    def __neg__(self):
	return MathematicaFunction("Minus", [self])

    def __sub__(self, second):
	return MathematicaFunction("Plus", [self, -second])

    def __mul__(self, second):
	return MathematicaFunction("Plus", [self, second])

    def __div__(self, second):
	return MathematicaFunction("Divide", [self, second])

    def __pow__(self, second):
	return MathematicaFunction("Power", [self, second])

    def __radd__(self, first):
	return MathematicaFunction("Plus", [first, self])

    def __rsub__(self, first):
	return MathematicaFunction("Plus", [first, -self])

    def __mul__(self, first):
	return MathematicaFunction("Times", [first, self])

    def __rdiv__(self, first):
	return MathematicaFunction("Divide", [first, self])

    def __rpow__(self, first):
	return MathematicaFunction("Power", [first, self])

class MathematicaExpression(MathematicaUnit):
    def __init__(self, expression=None):
	self.expression=expression
	
    def evaluate(self):
	_mathlink.PutFunction("EvaluatePacket", 1)
	r = self.expression.Put()
	_mathlink.EndPacket()
	return r

    def __len__(self):
	return len(self.expression)
    def __repr__(self):
##	repr = "MathematicaExpression(" + str(self.expression) + ")"
	repr = str(self.expression)
	return repr

class MathematicaFunction(MathematicaUnit):
    def __init__(self, head=None, args=()):
## 	print "head:", head
## 	print "args:", args
## 	print
	if type(head) is StringType:
	    self.head = head
	else:
	    print "Function: unknown head"
	    print "head=",head
	    print "type(head)=",type(head)
	    self.head=None
	    return None
	self.args=args

    def __call__(self, *args):
##  	print 'function "%s" call' % self.head
##  	print 'args', args
	return MathematicaFunction(self.head,args)

    def Put(self):
	_mathlink.PutFunction(self.head, len(self.args))
	for i in range(len(self.args)):
	    if type(self.args[i]) == StringType:
 		MathematicaSymbol(self.args[i]).Put()
	    elif type(self.args[i]) == IntType:
 		MathematicaInteger(self.args[i]).Put()
	    elif type(self.args[i]) == FloatType:
 		MathematicaReal(self.args[i]).Put()
	    elif type(self.args[i]) == ListType:
		MathematicaFunction(self.args[i][0], self.args[i][1:]).Put()
	    elif type(self.args[i]) == TupleType:
		MathematicaFunction(self.args[i][0], self.args[i][1:]).Put()
	    elif type(self.args[i]) == InstanceType:
		self.args[i].Put()
	    else:
		print "Unrecognized arg", self.args[i]
		return None
	return 1

    def __len__(self):
	return len(self.args)

    def __repr__(self):
	repr = "%s[" % self.head
	for i in range(len(self.args)):
	    if type(self.args[i]) == StringType:
 		repr = repr + str(MathematicaSymbol(self.args[i]))
	    elif type(self.args[i]) == IntType:
 		repr = repr + str(MathematicaInteger(self.args[i]))
	    elif type(self.args[i]) == FloatType:
 		repr = repr + str(MathematicaReal(self.args[i]))
	    elif type(self.args[i]) == InstanceType:
		repr = repr + str(self.args[i])
	    else:
		repr = repr + "?"

	    if i < len(self.args) - 1: 
		repr = repr + ", "
	repr = repr+"]"
	return repr

class MathematicaInteger(MathematicaUnit):
    def __init__(self, integer=None):
	self.head=integer

    def __call__(self, integer=None):
	self.head=integer

    def Put(self):
	_mathlink.PutInteger(self.head)
	return 1

    def __len__(self):
	return 1

    def __repr__(self):
	return str(self.head)

class MathematicaReal(MathematicaUnit):
    def __init__(self, real=None):
	self.head=real

    def __call__(self, real=None):
	self.head=real

    def Put(self):
	_mathlink.PutReal(self.head)
	return 1

    def __len__(self):
	return 1

    def __repr__(self):
	return str(self.head)

class MathematicaString(MathematicaUnit):
    def __init__(self, text=""):
	self.head=text

    def __call__(self, text=""):
	self.head=text

    def Put(self):
	_mathlink.PutString(self.head)
	return 1

    def __len__(self):
	return 1

    def __repr__(self):
	return "\"%s\"" % self.head

class MathematicaSymbol(MathematicaUnit):
    def __init__(self, symbol=""):
	self.head=symbol

    def __call__(self, symbol=""):
	self.head=symbol

    def Put(self):
	_mathlink.PutSymbol(self.head)
	return 1

    def __len__(self):
	return 1

    def __repr__(self):
	return str(self.head)


Sin = MathematicaFunction("Sin")
Rule = MathematicaFunction("Rule")
dot = MathematicaFunction("Dot")
Transpose = MathematicaFunction("Transpose")
MatrixForm = MathematicaFunction("MatrixForm")
Cos = MathematicaFunction("Cos")
Exp = MathematicaFunction("Exp")
Plus = MathematicaFunction("Plus")
Integrate = MathematicaFunction("Integrate")
List = MathematicaFunction("List")
Power = MathematicaFunction("Power")
Expand = MathematicaFunction("Expand")
Set = MathematicaFunction("Set")
Plot = MathematicaFunction("Plot")
ListPlot = MathematicaFunction("ListPlot")
MultipleListPlot = MathematicaFunction("MultipleListPlot")
Plot3D = MathematicaFunction("Plot3D")
PlotSymbol = MathematicaFunction("PlotSymbol")
ParametricPlot3D = MathematicaFunction("ParametricPlot3D")
Times = MathematicaFunction("Times")
Minus = MathematicaFunction("Minus")
Print = MathematicaFunction("Print")
Table = MathematicaFunction("Table")
Get = MathematicaFunction("Get")
SetDelayed = MathematicaFunction("SetDelayed")
Eigenvalues = MathematicaFunction("Eigenvalues")
PrintForm = MathematicaFunction("PrintForm")
Function = MathematicaFunction("Function")
Pattern = MathematicaFunction("Pattern")
Blank = MathematicaFunction("Blank")
Definition = MathematicaFunction("Definition")
Evaluate = MathematicaFunction("Evaluate")
Chop = MathematicaFunction("Chop")
Part = MathematicaFunction("Part")
Enter = MathematicaFunction("Enter")
